package com.zlz.m3u8download.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zlz.m3u8download.bean.TaskCacheResult;
import com.zlz.m3u8download.bean.entity.CacheM3u8Entity;
import com.zlz.m3u8download.bean.enums.CacheM3u8State;
import com.zlz.m3u8download.bean.enums.M3u8Type;
import com.zlz.m3u8download.config.M3u8Config;
import com.zlz.m3u8download.mapper.CacheM3u8Mapper;
import com.zlz.m3u8download.service.CacheTaskService;
import com.zlz.m3u8download.service.TaskService;
import com.zlz.m3u8download.util.FileUtil;
import com.zlz.m3u8download.util.M3u8FileUtil;
import com.zlz.m3u8download.util.OkHttpUtil;
import com.zlz.m3u8download.util.RandomGenerator;
import lombok.extern.slf4j.Slf4j;
import net.logstash.logback.util.StringUtils;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.rmi.RemoteException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import static com.zlz.m3u8download.bean.enums.CacheM3u8State.CACHED_COMPLETED;

/**
 * @Author: zhulinzhong
 * @Date: 2024/12/4
 */
@Service
@Slf4j
public class CacheTaskServiceImpl extends ServiceImpl<CacheM3u8Mapper, CacheM3u8Entity> implements CacheTaskService {

    private final CacheM3u8Mapper cacheM3u8Mapper;

    private final TaskService taskService;

    private final M3u8Config config;

    public CacheTaskServiceImpl(CacheM3u8Mapper cacheM3u8Mapper, TaskService taskService, M3u8Config config) {
        this.cacheM3u8Mapper = cacheM3u8Mapper;
        this.taskService = taskService;
        this.config = config;
    }

    @Override
    public TaskCacheResult fastCache(String url, Long userId) throws IOException {
        TaskCacheResult cacheResult = TaskCacheResult.builder().build();
        // 查询库
        CacheM3u8Entity cacheM3u8Entity = cacheM3u8Mapper.selectOne(Wrappers.<CacheM3u8Entity>lambdaQuery().eq(CacheM3u8Entity::getSourceUrl, url));
        if (cacheM3u8Entity != null) {
            cacheResult.setTaskId(cacheM3u8Entity.getId().toString());
            cacheResult.setUrl(cacheM3u8Entity.getUrl());
            cacheResult.setState(cacheM3u8Entity.getState());
            if (CACHED_COMPLETED.getValue().equals(cacheM3u8Entity.getState())) {
                return cacheResult;
            }
            return taskService.startTaskForPlay(cacheM3u8Entity, userId);
        }
        List<CacheM3u8Entity> cacheTask = createCacheTask(null, url, true, userId);
        if (cacheTask.isEmpty()) {
            throw new RemoteException("任务创建失败");
        }
        cacheResult.setTaskId(cacheTask.get(1).getId().toString());
        cacheResult.setUrl(cacheTask.get(0).getUrl());
        cacheResult.setState(cacheTask.get(0).getState());
        return cacheResult;
    }

    @Override
    public List<CacheM3u8Entity> createCacheTask(String vid, String url, boolean fast, Long userId) throws IOException {
        List<String> source = new ArrayList<>();
        if (StringUtils.isEmpty(url)) {
            source.addAll(genCmsUrl(vid));
        } else {
            source.add(url);
        }
        List<CacheM3u8Entity> cache = new ArrayList<>();
        for (String sourceUrl : source) {
            // 下载文件
            String sourceContent = OkHttpUtil.get(sourceUrl);
            // 计算MD5
            String sourceMd5 = FileUtil.md5(sourceContent.getBytes(StandardCharsets.UTF_8));
            M3u8Type m3u8Type = checkM3u8Type(sourceContent);
            CacheM3u8Entity m3u8Entity = CacheM3u8Entity.builder()
                    .type(m3u8Type.getValue())
                    .md5(sourceMd5)
                    .sourceUrl(sourceUrl)
                    .sourceContent(sourceContent)
                    .path(FileUtil.genTimeFolder() + "/" + RandomGenerator.gen(12) + "/")
                    .state(CacheM3u8State.INITIALIZED.getValue())
                    .createdTime(LocalDateTime.now())
                    .lastModifiedTime(LocalDateTime.now())
                    .build();
            m3u8Entity.setUrl(FileUtil.buildUrl(config.domainUrl, m3u8Entity.getPath() + config.m3u8Filename));
            List<CacheM3u8Entity> videos = buildContent(m3u8Entity);
            cache.add(m3u8Entity);
            cache.addAll(videos);
        }

        if (fast) {
            taskService.startTaskForPlay(cache.get(0), userId);
        }
        return cache;
    }


    private List<CacheM3u8Entity> buildContent(CacheM3u8Entity m3u8Entity) throws IOException {
        List<CacheM3u8Entity> videos = new ArrayList<>();
        // 列表类型的先构建video
        if (M3u8Type.LIST.getValue().equals(m3u8Entity.getType())) {
            // 保存列表获取id
            cacheM3u8Mapper.insert(m3u8Entity);
            videos.addAll(buildCacheM3u8VideoEntity(m3u8Entity));
            // 文件保存
            FileUtil.save(config.localPath, m3u8Entity.getPath() + config.m3u8Filename, m3u8Entity.getContent().getBytes(StandardCharsets.UTF_8));
            log.info("本地列表保存成功：{}", config.localPath + m3u8Entity.getPath() + config.m3u8Filename);
            cacheM3u8Mapper.updateById(m3u8Entity);
        } else {
            videos.add(m3u8Entity);
        }
        for (CacheM3u8Entity video : videos) {
            buildVideoContent(video);
        }
        this.saveBatch(videos);
        return videos;
    }

    private void buildVideoContent(CacheM3u8Entity video) throws IOException {
        StringBuilder videoM3u8 = new StringBuilder();
        List<String> rows = M3u8FileUtil.split(video.getSourceContent());
        int start = 10001;
        for (String row : rows) {
            if (row.startsWith("#")) {
                videoM3u8.append(row).append("\r\n");
                continue;
            }
            videoM3u8.append(config.domainUrl)
                    .append(video.getPath()).append(start++)
                    .append("_").append(RandomGenerator.gen(6))
                    .append(config.suffix).append("\r\n");
        }
        videoM3u8.append("#EXT-X-ENDLIST").append("\r\n");

        // 信息落库
        video.setContent(videoM3u8.toString());
        video.setLastModifiedTime(LocalDateTime.now());

        // 文件保存
        FileUtil.save(config.localPath, video.getPath() + config.m3u8Filename, video.getContent().getBytes(StandardCharsets.UTF_8));
        log.info("本地video保存成功：{}", config.localPath + video.getPath() + config.m3u8Filename);

    }

    public List<CacheM3u8Entity> buildCacheM3u8VideoEntity(CacheM3u8Entity list) throws IOException {
        List<String> rows = M3u8FileUtil.split(list.getSourceContent());
        List<CacheM3u8Entity> videos = new ArrayList<>();
        List<String> start = new ArrayList<>();
        for (int i = 0; i < rows.size(); i++) {
            String row = rows.get(i);
            if (M3u8FileUtil.isNotPath(row)) {
                start.add(row);
                continue;
            }
            String url = FileUtil.buildUrl(list.getSourceUrl(), row);
            // 下载资源文件
            String sourceContent = OkHttpUtil.get(url);
            // 构建video下载信息
            CacheM3u8Entity video = CacheM3u8Entity.builder()
                    .parentId(list.getId())
                    .type(M3u8Type.VIDEO.getValue())
                    .md5(FileUtil.md5(sourceContent.getBytes(StandardCharsets.UTF_8)))
                    .sourceUrl(url)
                    .simple(rows.get(i - 1))
                    .sourceContent(sourceContent)
                    .path(list.getPath() + M3u8FileUtil.videoVersion(null) + RandomGenerator.gen(5) + "/")
                    .state(CacheM3u8State.INITIALIZED.getValue())
                    .createdTime(LocalDateTime.now())
                    .lastModifiedTime(LocalDateTime.now())
                    .build();
            video.setUrl(FileUtil.buildUrl(config.domainUrl, video.getPath() + config.m3u8Filename));
            videos.add(video);
        }
        start.remove(start.size() - 1);
        // 设置m3u8列表文件信息
        list.setContent(
                M3u8FileUtil.genM3u8List(videos.stream().collect(
                        Collectors.toMap(CacheM3u8Entity::getSimple, this::getFileUrl)
                ), start)
        );
        return videos;
    }

    private List<String> genCmsUrl(String vid) {
        return Collections.emptyList();
    }

    /**
     * 获取任务类型
     *
     * @param content content
     * @return M3u8Type
     */
    private M3u8Type checkM3u8Type(String content) {
        for (String row : M3u8FileUtil.split(content)) {
            if (M3u8FileUtil.isNotPath(row)) {
                continue;
            }
            return M3u8FileUtil.isM3u8(row) ? M3u8Type.LIST : M3u8Type.VIDEO;
        }
        return M3u8Type.VIDEO;
    }

    public String getFileUrl(CacheM3u8Entity cacheM3u8Entity) {
        return config.domainUrl + cacheM3u8Entity.getPath() + config.m3u8Filename;
    }

}
